from Class.Serial_Com import Serial_com
import threading, sys
from time import sleep
from Class.CSConfig import ONHConfig
from Class.StandardMode import *
import copy
from Class.UserInfo import *
from Class.Format import Serialization, Format
from Class.DeviceBase import *
from Class.VoltSignal import *


class DAQ(object):
    def __init__(self):
        # region DO
        #载气切换阀
        self.ValveCarrierSwitch = 4
        # 载气阀
        self.ValveCarrier = 5
        # 排放阀，气路出口
        self.ValveCutoff = 3
        # 开关炉 0关1开
        self.ValveFurnace = 9
        # 投样
        self.ValveSampleDrop = 8
        # 加样
        self.ValveSampleLoad = 7
        # 封顶,0封；1开
        self.ValveSeal = 6
        # 参比气阀
        self.ValveCompare = 13
        # 炉气阀  1：闭合  0 打开
        self.ValveChamber = 12
        # 旁路阀 0旁路气路
        self.ValveBypass = 11
        # 冲洗阀  1：冲洗  0 断开
        self.ValvePurge = 10
        self.ValveTCBridge = 16
        # endregion
        # region DI
        # 炉子开合状态 true关false开
        self.DIFurnace = 4
        # endregion
        # region DA
        self.DASCR = 0
        self.DAFlow = 2
        self.DALE1 = -1
        self.DAHE1 = -1
        self.DALE2 = 1
        self.DALE3 = -1
        self.DALE3TC = 1
        self.DAFLowSet = 4
        # endregion
        # region AD
        # 电压
        self.ADVolt = 32
        # 电流
        self.ADCurrent = 33
        # 水温
        self.ADWaterT = 38
        self.ADWaterF = 37
        self.ADWaterPressure = 39
        self.ADFurnaceT = 34
        # 转化炉温
        self.ADReformer = 41
        # 净化炉温
        self.ADPurification = 42
        self.ADPressure = 35
        # 流量
        self.ADFlow = 36
        # 热导温度
        self.ADTCT = 40
        self.ADLE1 = 0
        self.ADHE1 = 1
        self.ADLE2 = 17
        self.ADLE3 = 2
        self.ADLE3TC = 17
        self.ADLE1Inverse = False
        self.ADHE1Inverse = False
        self.ADLE2Inverse = True
        self.ADLE3Inverse = False
        self.ADLE3TCInverse = False
        # 采集频率Hz
        self.freq = 5.0
        self.filterC = 1  # 滤波系数
        self.com = '/dev/ttyUSB0'


class AlarmThreshold():
    def __init__(self):
        self.volt = 7.0
        self.current = 1300
        self.waterT = 45
        self.carrierpressure = 0.1
        self.carrierflow = 100
        self.waterF = 1
        self.reformerdown = 400
        self.reformerup = 600
        self.purificationdown = 400
        self.purficationup = 600


from enum import Enum


class SystemConfig(object):
    def __init__(self):
        self.resultcolHidden = []
        self.userparams = {Userparam.Current: [0, 300, 200], Userparam.Volt: [0, 2, 0],
                           Userparam.WaterT: [0, 12.5, -45],
                           Userparam.Reformer: [0, 100, 0], Userparam.Purification: [0, 100, 0],
                           Userparam.GasPressure: [0, 0.05, -0.025], Userparam.Folw: [0, 200, 0],
                           Userparam.Initcurrent: [0, 0, 0], Userparam.Initvolt: [0, 0, 0],
                           Userparam.Waterspeed: [0, 1.864, -5.75], Userparam.Waterpressure: [0, 0.125, -0.25]}

        self.piuItems = []
        self.flowset = 1.1
        self.daq = DAQ()
        self.alarmthreshold = AlarmThreshold()
        self.k1 = 3
        self.k2 = 2
        self.k3 = 1
        self.p = 1 / 15000 * 0.7
        self.ki1 = 8
        self.ki2 = 2
        self.ki3 = 1


class ONH(DeviceBase):

    def __init__(self):
        super().__init__()
        self.tcBridgeCloseCont = 0  # 桥流自动关闭的计数
        self.tcBridgeOpenCont = 0  # 桥流自动开启的计数
        self.tcBridgeControl = True  # True 控制桥流自动打开，在检漏时，需要设为False
        self.monitor = MonitorONH()
        self.card = Serial_com()
        self.card.sigReceive.connect(self.monitorRefresh)
        self.card.sigReceive.connect(self.baselineControl)
        self.sigBaseLineUpdate.connect(self.dataupdate)
        self.config = ONHConfig()
        self.systemconfig = SystemConfig()
        inPutPath = os.path.abspath(sys.argv[0])
        self.configFolder = os.path.join(os.path.dirname(inPutPath), "ONH")
        self.configDefaultName = "default.bin"
        self.outvolt = 0.0  # 当前输出电压
        self.preOutVolt = 0.0
        self.deltaPrevPower = 0.0
        self.deltaPrevPrevPower = 0.0
        self.analyseStop = True
        self.obtainResults = False
        self.enableAcq = False  # 开始采集释放曲线
        self.modelResult = ResultMode()
        self.baseData = BaseData()  # 基线状态
        self.baseData.ek = [0, 0, 0, 0, 0, 0]
        self.baseData.ei = [0, 0, 0, 0, 0, 0]
        self.baseData.ed = [0, 0, 0, 0, 0, 0]

        self.vs = VoltSignal()
        self.acqEvent = None
        self.bRefreshVimax = True  # True 更新Vimax,当调试页面查看调零后基线时或进入等待状态后，不再更新
        self.sampleMode = SampleMode.ModeSampling
        self.autoStatus = 0  # 0初始状态，1已降炉，2已升炉等待加样3已加样等待分析4分析中

    # region 配置
    def deinit(self):
        """将方法、系统设置二进制序列化到文件"""
        self.savesystemconfig()
        self.saveconfig()
        dataBase.closeDB()

    def saveconfig(self):
        """序列化方法到文件，并记录当前方法名"""
        filepath = os.path.join(self.configFolder, self.config.name + ".bin")
        Serialization.serializeToFile(filepath, self.config)
        defaultpath = os.path.join(self.configFolder, "default.txt")
        with open(defaultpath, "w") as f:
            f.write(self.config.name)

    def savesystemconfig(self):
        """序列化系统配置到文件"""
        filepath = os.path.join(self.configFolder, "systemconfig.bin")
        Serialization.serializeToFile(filepath, self.systemconfig)

    def init(self, path):
        """
        仪器配置初始化：判断数据文件是否正常，加载方法文件
        :param path:数据文件存储路径
        :return:True 初始化成功
        """
        dbPath = os.path.join(path, "ONH")
        if dataBase.initDB(dbPath):
            self.getsystemconfig()
            filepath = os.path.join(self.configFolder, "default.txt")
            config = None
            if os.path.exists(filepath):
                with open(filepath, 'r') as file:
                    method = file.readline()
                    if len(method) != 0:
                        config = self.getconfig(method)
            if config is not None:
                self.config = config
                Format.decimals = self.config.decimals
            else:  # 第一次登录，先保存下配置文件
                self.deinit()
            return True
        else:
            return False

    def getsystemconfig(self):
        """反序列化得到系统配置"""
        config = None
        filepath = os.path.join(self.configFolder, "systemconfig.bin")
        config = Serialization.deserialize(filepath)
        if config is not None:
            self.systemconfig = config

    def getconfig(self, name):
        """
        反序列化方法文件（因为此种反序列化方式不会因为原类发生变化，而自动更新，所以只能依次更新属性）
        :param name:方法名
        :return: 方法对象，序列化失败或者文件不存在，返回None
        """
        config = ONHConfig()
        filepath = os.path.join(self.configFolder, name + ".bin")
        configtemp = Serialization.deserialize(filepath)
        if configtemp is not None:
            # region 新增、删除属性
            for name, value in vars(configtemp).items():
                if hasattr(config, name):
                    if name == "factors":
                        for index, factor in enumerate(value):
                            for propertyname, propertyvalue in vars(factor).items():
                                if hasattr(config.factors[index], propertyname):
                                    setattr(config.factors[index], propertyname, propertyvalue)
                    elif name == "baseItems":
                        for index, channelData in enumerate(value):  # 每个通道
                            if len(channelData) > 0:
                                for baseItem in channelData:
                                    item = BaseCalibrationItem()
                                    for propertyname, propertyvalue in vars(baseItem).items():
                                        if hasattr(item, propertyname):
                                            setattr(item, propertyname, propertyvalue)
                                    config.baseItems[index].append(item)
                    else:
                        setattr(config, name, value)
            # region 新增、删除属性
            # for name, value in vars(configtemp).items():
            #     if not hasattr(config, name):
            #         setattr(config, name, value)
            # delattrlist = []
            # for name, value in vars(config).items():
            #     if not hasattr(configtemp, name):
            #         delattrlist.append(name)
            # for i in delattrlist:
            #     delattr(config, i)
            # if not hasattr(config.factors[0], "blankarea"):
            #     for i in range(dataBase.maxChannel):
            #         setattr(config.factors[i], "blankarea", 0)
            # endregion
        return config

    # endregion

    def findInUserparams(self, key: Userparam):
        """根据key查找仪器参数"""
        params = [0, 1, 0]
        try:
            params = self.systemconfig.userparams[key]
        except:
            pass
        return params

    def updateUserparams(self, key: Userparam, data: []):
        """更新仪器参数"""
        self.systemconfig.userparams[key] = data

    def getPIUItems(self):
        """获取功率校正数据"""
        return self.systemconfig.piuItems

    def setPIUItems(self, s):
        """设置功率校正数据"""
        self.systemconfig.piuItems = s

    def getADBaseline(self, i):
        if i < 4:
            if self.config.thermalH:
                ADBaseline = [self.systemconfig.daq.ADLE1, self.systemconfig.daq.ADHE1, self.systemconfig.daq.ADLE2,
                              self.systemconfig.daq.ADLE3TC]
            else:
                ADBaseline = [self.systemconfig.daq.ADLE1, self.systemconfig.daq.ADHE1, self.systemconfig.daq.ADLE2,
                              self.systemconfig.daq.ADLE3]
            return ADBaseline[i]
        return 0

    def getDAZero(self, i):
        if i < 4:
            if self.config.thermalH:
                DAZero = [self.systemconfig.daq.DALE1, self.systemconfig.daq.DAHE1, self.systemconfig.daq.DALE2,
                          self.systemconfig.daq.DALE3TC]
            else:
                DAZero = [self.systemconfig.daq.DALE1, self.systemconfig.daq.DAHE1, self.systemconfig.daq.DALE2,
                          self.systemconfig.daq.DALE3]
            return DAZero[i]
        return 0

    def getADBaselineInverse(self, i):
        if i < 4:
            if self.config.thermalH:
                ADBaselineInverse = [self.systemconfig.daq.ADLE1Inverse, self.systemconfig.daq.ADHE1Inverse,
                                     self.systemconfig.daq.ADLE2Inverse, self.systemconfig.daq.ADLE3TCInverse]
            else:
                ADBaselineInverse = [self.systemconfig.daq.ADLE1Inverse, self.systemconfig.daq.ADHE1Inverse,
                                     self.systemconfig.daq.ADLE2Inverse, self.systemconfig.daq.ADLE3Inverse]
            return ADBaselineInverse[i]
        return 0

    def getNeedZero(self, i):
        """获取是否需要调零，需要调零的通道，不需要做调零算法转换"""
        if i < 4:
            if self.config.thermalH:
                needzero = [False, False, False, True, ]
            else:
                needzero = [False, False, True, False, ]
            return needzero[i]
        return 0

    def getBaseDataPID(self, i):
        if i < 4:
            if self.config.thermalH:
                baseDataPID = [{0.02, 0.1, 0, -10}, {0.02, 0.1, 0, -10}, {0.005, 0.025, 0, -10},
                               {0.005, 0.025, 0, -10}, ]
            else:
                baseDataPID = [{0.02, 0.1, 0, -10}, {0.02, 0.1, 0, -10}, {0.005, 0.025, 0, -10}, {0.02, 0.1, 0, -10}, ]
            return baseDataPID[i]
        return 0

    def monitorRefresh(self, pts):
        """更新仪器状态参数"""
        self.monitor.furnaceStatus = self.cardGetDI(pts.DIValue, self.systemconfig.daq.DIFurnace)
        v = pts.ChannelV[self.systemconfig.daq.ADCurrent]
        params = self.findInUserparams(Userparam.Current)
        current = v ** 2 * params[0] + v * params[1] + params[2]
        if current < 0 or v < self.findInUserparams(Userparam.Initcurrent)[2]:
            self.monitor.current = 0
        else:
            self.monitor.current = current
        params = self.findInUserparams(Userparam.Volt)
        v = pts.ChannelV[self.systemconfig.daq.ADVolt]
        volt = v * params[1] + params[2]
        if volt < 0 or v < self.findInUserparams(Userparam.Initvolt)[2]:
            self.monitor.volt = 0
        else:
            self.monitor.volt = volt
        self.monitor.power = self.monitor.volt * self.monitor.current
        params = self.findInUserparams(Userparam.Folw)
        self.monitor.carrierflow = pts.ChannelV[self.systemconfig.daq.ADFlow] * params[1] + params[2]
        params = self.findInUserparams(Userparam.GasPressure)
        self.monitor.carrierpressure = pts.ChannelV[self.systemconfig.daq.ADPressure] * params[1] + params[2]
        params = self.findInUserparams(Userparam.WaterT)
        waterT = pts.ChannelV[self.systemconfig.daq.ADWaterT] * params[1] + params[2]
        self.monitor.waterT = 0 if waterT < 0 else waterT
        params = self.findInUserparams(Userparam.Waterspeed)
        waterspeed = pts.ChannelV[self.systemconfig.daq.ADWaterF] * params[1] + params[2]
        self.monitor.waterspeed = 0 if waterspeed < 0 else waterspeed
        params = self.findInUserparams(Userparam.Waterpressure)
        waterpressure = pts.ChannelV[self.systemconfig.daq.ADWaterPressure] * params[1] + params[2]
        self.monitor.waterpressure = 0 if waterpressure < 0 else waterpressure
        params = self.findInUserparams(Userparam.Purification)
        purification = pts.ChannelV[self.systemconfig.daq.ADPurification] * params[1] + params[2]
        self.monitor.purification = 0 if purification < 0 else purification
        params = self.findInUserparams(Userparam.Reformer)
        reformer = pts.ChannelV[self.systemconfig.daq.ADReformer] * params[1] + params[2]
        self.monitor.purification = 0 if reformer < 0 else reformer

        self.monitor.errorvolt = self.monitor.volt > self.systemconfig.alarmthreshold.volt
        self.monitor.errorCurrent = self.monitor.current > self.systemconfig.alarmthreshold.current
        self.monitor.errorcarrier = self.monitor.carrierflow < self.systemconfig.alarmthreshold.carrierflow
        self.monitor.errorwaterF = self.monitor.waterspeed < self.systemconfig.alarmthreshold.waterF
        self.monitor.errorwaterT = self.monitor.waterT > self.systemconfig.alarmthreshold.waterT
        self.monitor.errorcarrierpressure = self.monitor.carrierpressure < self.systemconfig.alarmthreshold.carrierpressure
        self.monitor.errorpurification = True if self.monitor.purification > self.systemconfig.alarmthreshold.purficationup or \
                                                 self.monitor.purification < self.systemconfig.alarmthreshold.purificationdown else False
        self.monitor.errorreformer = True if self.monitor.reformer > self.systemconfig.alarmthreshold.reformerup or \
                                             self.monitor.reformer < self.systemconfig.alarmthreshold.reformerdown else False
        if self.monitor.errorcarrier and self.getTCBridge():#载气流量报警时，若当前桥流是打开状态，则持续15点后，关闭桥流
            self.tcBridgeCloseCont += 1
            if self.tcBridgeCloseCont > 15:
                self.controlTCBridge(False)
        else:
            self.tcBridgeCloseCont = 0
        # 非检漏模式下，流量正常，且一直没有打开桥流，则等75个点后打开桥流
        if self.tcBridgeControl and (not self.monitor.errorcarrier) and (not self.getTCBridge()):
            self.tcBridgeOpenCont += 1
            if self.tcBridgeOpenCont > 75:
                self.controlTCBridge(True)
        else:
            self.tcBridgeOpenCont =0

    def baselineControl(self, pts):
        '''基线处理：判断稳定、求均值、调零,释放基线更新信号'''
        baseline_init = []#原始基线
        for i in range(dataBase.maxChannel):
            baseline_init.append(pts.ChannelV[self.getADBaseline(i)])
            # 滤波处理
            # if self.baseline_last[i] == -99:
            #         self.baseline_last[i] = baseline_list[i]
            # if self.systemconfig.daq.filterC != 1:
            #     baseline_list[i] = self.systemconfig.daq.filterC * baseline_list[i] + \
            #                        (1-self.systemconfig.daq.filterC) * self.baseline_last[i]
            # self.baseline_last[i] = baseline_list[i]
        baseline_zero = [0 for i in range(dataBase.maxChannel)]  # 处理后
        for i in range(dataBase.maxChannel):
            if self.config.channels[i].enable:
                # 若启用通道，则调零、求均值
                # <editor-fold desc="非释放曲线状态下，记录定长基线，计算均值">
                if not self.enableAcq and self.bRefreshVimax:
                    self.baseData.channels[i].data.append(baseline_init[i])
                    while len(self.baseData.channels[i].data) > self.systemconfig.daq.freq * self.config.baselineTime:
                        self.baseData.channels[i].data[:-1] = self.baseData.channels[i].data[1:]
                        self.baseData.channels[i].data.pop()
                    self.baseData.channels[i].stable += 1
                    self.baseData.channels[i].average = Factor.calculateAverage(self.baseData.channels[i].data)
                else:
                    self.baseData.channels[i].data.clear()
                # </editor-fold>
                # <editor-fold desc="处理基线数据，计算均值">
                if self.getNeedZero(i):
                    baseline_zero[i] = baseline_init[i]
                else:
                    baseline_zero[i] = Factor.convertToZeroV(self.baseData.channels[i].average, baseline_init[i])
                # 非释放曲线状态，一直计算基线均值
                if not self.enableAcq:
                    self.baseData.channels[i].data0.append(baseline_zero[i])
                    while len(self.baseData.channels[i].data0) > self.systemconfig.daq.freq * self.config.baselineTime:
                        self.baseData.channels[i].data0[:-1] = self.baseData.channels[i].data0[1:]
                        self.baseData.channels[i].data0.pop()
                    self.baseData.channels[i].average0 = Factor.calculateAverage(self.baseData.channels[i].data0)
                else:
                    self.baseData.channels[i].data0.clear()
                # </editor-fold>
                self.baseData.ek[i] = baseline_zero[i]
                if self.getNeedZero(i):
                    if abs(baseline_zero[i] * 1000) > self.config.baselineDeviation:
                        self.baseData.channels[i].stable = 0
                        # <editor-fold desc="调零">
                        if self.baseData.isTest:
                            dac = self.baseData.channels[i].dac
                            pid = self.getBaseDataPID(i)
                            deltaP = pid[0] * self.baseData.ek[i] - \
                                     pid[1] * self.baseData.ei[i] + \
                                     pid[2] * self.baseData.ed[i]
                            dac += deltaP
                            dac = min(dac, 10)
                            dac = max(dac, self.baseData.PID[i][3])
                            self.baseData.channels[i].dac = dac
                            self.controlZero(i, dac)
                        # </editor-fold>
        data = [pts.UpContainer, baseline_init, baseline_zero]
        self.sigBaseLineUpdate.emit(data)
        self.baselineCheckStability()

    def baselineCheckStability(self):
        '''判断基线是否稳定：在指定时间内，都处于偏差范围内'''
        count = self.config.baselineTime * self.systemconfig.daq.freq
        stable = True
        for i in range(dataBase.maxChannel):
            if self.config.channels[i].enable and self.baseData.channels[i].stable < count:
                stable = False
        self.baseData.isStable = stable

    def tipAndSleep(self, s, time=0):
        """
        触发sigStatusTip信号，延迟time*1000s
        :param s:传递的信息
        :param time:延迟时间，单位s,默认不延迟
        """
        self.sigStatusTip.emit(s)
        if time > 0:
            s = time / 10.0
            for i in range(10):
                sleep(s)
                self.sigWaitting.emit(i + 1)

    # region 仪器控制
    def analyseStart(self, sampleInfo, sampleMode=SampleMode.ModeSampling):
        self.analyseStop = False
        self.obtainResults = True
        self.acqEvent = None
        self.bHeat = False #记录是否已进入加热环节，True时，结束时需要等待坩埚冷却
        self.vs = VoltSignal()
        self.vs.result.SampleName = sampleInfo.sampleName
        self.vs.result.SampleNum = sampleInfo.sampleNum
        self.vs.sampleId = datetime.datetime.now()
        self.vs.result.FluxWeight = sampleInfo.fluxWeight
        self.vs.result.SampleWeight = sampleInfo.sampleWeight
        self.vs.result.MethodName = self.config.name
        self.vs.result.UserName = currentuser
        self.vs.factors.clear()
        self.vs.channels.clear()
        for i in range(dataBase.maxChannel):
            self.vs.factors.append(copy.copy(self.config.factors[i]))
            self.vs.channels.append(self.config.channels[i].enable)
        self.sampleMode = sampleMode
        t = threading.Thread(target=self.demoTest)
        # 后台线程
        t.setDaemon(True)
        t.start()

    def demoTest(self):
        if self.sampleMode == SampleMode.ModeSampling:
            self.sigClearWeight.emit()
            self.autoAnalyse()
        if self.sampleMode == SampleMode.ModeManual:
            self.sigClearWeight.emit()
            self.manualAnalyse()
            pass
        if self.sampleMode == SampleMode.ModeProcess:
            self.obtainResults = False
            self.heatingCruible()

        self.obtainResult()
        self.reset()
        if self.bHeat:
            self.tipAndSleep("等待坩埚冷却", 20)
        self.tipAndSleep("分析结束")
        self.autoStatus = 0
        self.bRefreshVimax = True
        self.analyseStop = True
        self.baseData.enableTest()

    def analyse_stop(self):
        """停止分析"""
        self.analyseStop = True

    def initPID(self):
        """初始化PID参数,关闭可控硅"""
        self.preOutVolt = 0.0
        self.deltaPrevPower = 0.0
        self.deltaPrevPrevPower = 0.0
        self.controlSCR(0.0, True)

    def autoAnalyse(self):
        if not self.processCrucible():
            return
        self.gasSwitchAnalysis(True)
        if not self.analyseStop:
            self.obtainResults = True
            self.tipAndSleep("投样")
            self.cardOpenValve(self.systemconfig.daq.ValveSampleDrop)
            self.cardOutput()
            sleep(0.5)
            self.cardCloseValve(self.systemconfig.daq.ValveSampleDrop)
            self.cardOutput()
        self.analysis()

    def analysis(self):
        # TODO 基线停止绘制，清除原有释放曲线
        if not self.analyseStop:
            self.obtainResults = True
            self.tipAndSleep("开始分析")
            th = threading.Thread(target=self.TaskSampleAction)
            th.setDaemon(True)
            th.start()
            self.regulatePID(2, True)
            th.join()
        return True

    def heatingCruible(self):
        """预处理坩埚，不等基线稳定"""
        self.processCrucible(False, True)

    def manualAnalyse(self):
        self.processCrucible(False, True)
        self.analysis()

    def TaskSampleAction(self):
        self.acq()

    def gasSwitchAnalysis(self, analysis):
        """气路切换:分析-旁路"""
        if analysis:
            self.cardOpenValve(self.systemconfig.daq.ValveChamber,
                                self.systemconfig.daq.ValveBypass)
            self.cardCloseValve(self.systemconfig.daq.ValvePurge)
            self.cardOutput()
        else:
            self.cardCloseValve(self.systemconfig.daq.ValveBypass)
            self.cardOutput()

    def beginPurge(self):
        """切换到旁路状态，开启冲洗"""
        if self.card.isLinked:
            self.gasSwitchAnalysis(False)  # 切换到旁路
            self.cardOpenValve(self.systemconfig.daq.ValveChamber,
                               self.systemconfig.daq.ValvePurge)
            self.cardOutput()

    def endPurge(self):
        """结束冲洗"""
        if self.card.isLinked:
            self.cardCloseValve(self.systemconfig.daq.ValveChamber)
            self.cardOutput()

    def getTCBridge(self):
        """
        获取桥流状态
        :return: True 桥流已打开
        """
        return self.cardGetDO(self.systemconfig.daq.ValveTCBridge)

    def controlTCBridge(self, open: bool):
        """开关桥流"""
        if open:
            self.cardOpenValve(self.systemconfig.daq.ValveTCBridge)
        else:
            self.cardCloseValve(self.systemconfig.daq.ValveTCBridge)
        self.cardOutput()

    def controlZero(self, index, v):
        if index > 0:
            self.cardAO(self.getDAZero(index), v)

    def controlMFC(self, open, v=0, anyValue=False):
        if anyValue:
            if v > 0:
                self.cardAO(self.systemconfig.daq.DAFlow, v)
            else:
                if self.getTCBridge():
                    QMessageBox.warning(self, "提示", "请先关闭桥流")
        else:
            if open:
                self.cardAO(self.systemconfig.daq.DAFlow, self.systemconfig.daq.DAFLowSet)
            else:
                if self.getTCBridge():
                    self.controlTCBridge(False)
                    self.tipAndSleep("关闭桥流，等待15s", 15)
                self.cardAO(self.systemconfig.daq.DAFlow, 0)

    def controlSCR(self, value, delay=True):
        if not self.monitor.furnaceStatus:
            return
        if value < 0:
            value = 0
        if value > 10:
            value = 10
        self.outvolt = value
        self.cardAO(self.systemconfig.daq.DASCR, value, delay)

    def processCrucible(self, wait=True, powerout=True):
        """
        判断基线稳定，停止基线调零，冲洗脱气等待
        :param wait: 手动处理坩埚时为false,不等待基线稳定，不执行等待步骤
        :param powerout: 手动分析时为false,不输出功率
        :return:
        """
        if self.card.isLinked:
            return False
        self.cardOpenValve(self.systemconfig.daq.ValveCutoff)  # 开出口截止阀
        self.cardCloseValve(self.systemconfig.daq.ValveFurnace)  # 升炉
        self.cardOutput()
        sleep(0.3)
        self.controlMFC(True)
        self.tipAndSleep("等待基线稳定")
        while not (self.baseData.isStable or self.baseData.forceAnalyse) and wait:
            if self.analyseStop:
                return False
            sleep(0.5)
        self.baseData.disableTest()
        # <editor-fold desc="冲洗">
        self.tipAndSleep("冲洗")
        self.beginPurge()
        sleep(0.5)
        self.cardOpenValve(self.systemconfig.daq.ValveSeal)  # 松开封顶
        self.cardOutput()
        sleep(5)
        self.cardCloseValve(self.systemconfig.daq.ValveSeal)
        self.cardOutput()
        sleep(5)
        # </editor-fold>
        if not self.analyseStop:
            self.tipAndSleep("脱气")
            self.regulatePID(0, powerout)
        self.bRefreshVimax = False
        # 记录标准0浓度时的基线均值
        for i in range(dataBase.maxChannel):
            self.vs.average0[i] = self.baseData.channels[i].average0
        # 记录基准电压（调零前的）
        self.vs.setaverageV(i, self.baseData.channels[i].average)
        if not self.analyseStop and wait:
            self.gasSwitchAnalysis(True)
            self.tipAndSleep("等待")
            self.regulatePID(1, powerout)
        return True

    def regulatePID(self, type, powerout):
        '''可控硅控制'''
        interval = 500
        realinterval = 400  # 因为发送控制命令时延迟了100ms
        for i, p in enumerate(self.config.powerArray):
            if self.analyseStop or p.type > type:
                break
            if p.type == type:
                time = p.timepower * 1000
                beginpower = p.beginpower
                endpower = p.endpower
                name = ""
                if type == 0:
                    name = "脱气"
                if type == 1:
                    name = "等待"
                if time > 0:
                    tickcount = 0
                    t = 0
                    bstopPID = False
                    while not bstopPID and not self.analyseStop:
                        t = tickcount * interval
                        tickcount += 1
                        if t > time:
                            bstopPID = True
                            break
                        if type < 2 and (time - t) % 1000 == 0:
                            self.tipAndSleep(name + str((time - t) / 1000) + "s")
                        if powerout:
                            power = beginpower + (endpower - beginpower) / time * t
                            if power > 0:
                                self.bHeat = True
                            self.PID(power, self.config.PIDStyle)
                        if not self.analyseStop:
                            sleep(realinterval / 1000.0)
        if type == 2 or self.analyseStop:
            self.initPID()

    def PID(self, pps, isPower: int):
        k1 = 0
        k2 = 0
        k3 = 0
        p = self.systemconfig.p
        power = 0.0
        if isPower == 0:
            k1 = self.systemconfig.k1
            k2 = self.systemconfig.k2
            k3 = self.systemconfig.k3
            power = self.monitor.power
        else:
            k1 = self.systemconfig.ki1
            k2 = self.systemconfig.ki2
            k3 = self.systemconfig.ki3
            power = self.monitor.current

        deltaPower = pps - power
        tmp = k1 * deltaPower - k2 * self.deltaPrevPower + k3 * self.deltaPrevPrevPower
        outvolt = self.preOutVolt + p * tmp
        if outvolt > 10:
            outvolt = 10
        elif outvolt < 0:
            outvolt = 0
        self.controlSCR(outvolt)
        self.deltaPrevPrevPower = self.deltaPrevPower
        self.deltaPrevPower = deltaPower
        self.preOutVolt = outvolt

    def acq(self):
        """采集"""
        self.acqCount = 0
        self.lastTick = 0
        self.lastTime = 0.0
        self.stop = [False for x in range(dataBase.maxChannel)]
        self.sumDelay = [0.0 for x in range(dataBase.maxChannel)]
        self.max = [0.0 for x in range(dataBase.maxChannel)]
        self.peakMax = 100
        self.integralDelayCount = 0
        # 设置通道全部打开
        self.vs.result.Channel = 0x3F
        for i in range(0, dataBase.maxChannel):
            # 记录基线均值，积分延迟时间内也会更新该值
            self.vs.average[i] = self.baseData.channels[i].average0
            if bool(1 - self.config.channels[i].enable):  # 关闭方法中未打开的通道
                self.stop[i] = True
                t = 1 << i
                t = ~t
                self.vs.result.Channel = self.vs.result.Channel & t
            elif (i == 1) and self.config.channels[0].enable:  # 高低同时打开时，关闭高通道
                t = 1 << i
                t = ~t
                self.vs.result.Channel = self.vs.result.Channel & t
        if not self.analyseStop:
            self.acqEvent = threading.Event()  # 用于停止线程的标识
            self.enableAcq = True  # 触发DataReceive 开始接收数据
            self.sigCurveLineAddPoint.emit(-1, 0, 0)  # -1 清理释放曲线
            self.acqEvent.wait()  # 暂停线程运行，等待set信号
        self.enableAcq = False
        self.analyseStop = True

    def reset(self):
        """每次分析完成后复位仪器到初始状态"""
        if self.card.isLinked:
            self.initPID()
            self.controlMFC(True)
            self.cardOpenValve(self.systemconfig.daq.ValveCarrier,
                               self.systemconfig.daq.ValveCompare,
                               self.systemconfig.daq.ValveCutoff)
            self.cardCloseValve(self.systemconfig.daq.ValveSampleDrop,
                                self.systemconfig.daq.ValveSampleLoad,
                                self.systemconfig.daq.ValveFurnace,
                                self.systemconfig.daq.ValveChamber)
            self.cardOutput()
            sleep(0.1)
            self.cardCloseValve(self.systemconfig.daq.ValveSeal)
            self.cardOutput()
            self.gasSwitchAnalysis(False)
            self.tipAndSleep("就绪")

    def initCarrierGas(self):
        """
        仪器上电复位仪器到初始状态，关闭桥流，针对炉头漏气现象，设计为先开封顶
        :return:
        """
        if self.card.isLinked:
            self.initPID()
            self.tcBridgeOpenCont = 0
            self.controlTCBridge(False)
            self.controlMFC(True)
            self.switchNH()
            self.cardOpenValve(self.systemconfig.daq.ValveCarrier,
                               self.systemconfig.daq.ValveCompare,
                               self.systemconfig.daq.ValveCutoff,
                               self.systemconfig.daq.ValveSeal)
            self.cardOutput()
            sleep(0.1)
            self.cardCloseValve(self.systemconfig.daq.ValveSampleDrop,
                                self.systemconfig.daq.ValveSampleLoad,
                                self.systemconfig.daq.ValveFurnace,
                                self.systemconfig.daq.ValveChamber)
            self.cardOutput()
            sleep(0.1)
            self.cardCloseValve(self.systemconfig.daq.ValveSeal)
            self.cardOutput()
            self.gasSwitchAnalysis(False)
            self.tipAndSleep("")

    def addSample(self):
        t = threading.Thread(target=self.taskAddSample)
        # 后台线程
        t.setDaemon(True)
        t.start()

    def taskAddSample(self):
        """
        加样
        :return:
        """
        self.beginPurge()
        sleep(0.5)
        self.cardOpenValve(self.systemconfig.daq.ValveSeal,
                           self.systemconfig.daq.ValveSampleLoad)
        self.cardOutput()
        self.tipAndSleep("加样",5)
        self.cardCloseValve(self.systemconfig.daq.ValveSampleLoad)
        self.cardOutput()
        sleep(3)
        self.cardCloseValve(self.systemconfig.daq.ValveSeal)
        self.cardOutput()
        sleep(0.5)
        self.endPurge()
        if self.autoStatus == 3:
            self.sigSetOnlyStartEnable.emit(True)
        if self.autoStatus == 0:
            self.sigSetStartEnable.emit(True)

    def furnaceOperation(self, type=0):
        self.gasSwitchAnalysis(False)
        furnaceStatus = self.monitor.furnaceStatus
        t = threading.Thread(target=self.furnaceUpDown, kwargs={"open": furnaceStatus})
        # 后台线程
        t.setDaemon(True)
        t.start()

    def furnaceUpDown(self, open: bool):
        """
        升降炉
        :param open:
        :return:
        """
        if open:
            self.tipAndSleep("降炉")
            self.cardCloseValve(self.systemconfig.daq.ValveChamber)
            self.cardOutput()
            sleep(0.5)
            self.cardOpenValve(self.systemconfig.daq.ValveFurnace)
            self.cardOutput()
            if self.autoStatus == 1:
                self.sigSetOnlyStartEnable.emit(True)
        else:
            self.tipAndSleep("关炉冲洗")
            self.beginPurge()
            self.cardCloseValve(self.systemconfig.daq.ValveFurnace)
            self.cardOutput()
            sleep(5)
            self.endPurge()
            if self.autoStatus == 2:
                self.sigSetOnlyStartEnable.emit(True)

    def clearFurnaceTask(self):
        self.gasSwitchAnalysis(False)
        if self.monitor.furnaceStatus:  # 炉子关闭，则开始清炉
            self.cardCloseValve(self.systemconfig.daq.ValveChamber)
            self.cardOpenValve(self.systemconfig.daq.ValveSeal,
                               self.systemconfig.daq.ValveSampleLoad,
                               self.systemconfig.daq.ValveSampleDrop,
                               self.systemconfig.daq.ValveFurnace)
            self.cardOutput()
        else:
            self.cardCloseValve(self.systemconfig.daq.ValveSampleLoad,
                                self.systemconfig.daq.ValveSampleDrop,
                                self.systemconfig.daq.ValveFurnace)
            self.cardOutput()
            self.tipAndSleep("冲洗")
            self.beginPurge()
            sleep(3)
            self.cardCloseValve(self.systemconfig.daq.ValveSeal)
            self.cardOutput()
            sleep(0.5)
            self.endPurge()
        self.sigSetStartEnable(True)

    def clearFurnace(self):
        t = threading.Thread(target=self.clearFurnaceTask)
        # 后台线程
        t.setDaemon(True)
        t.start()

    def dropSample(self):
        self.cardOpenValve(self.systemconfig.daq.ValveSampleDrop)
        self.cardOutput()
        self.tipAndSleep("投样", 1)
        self.cardCloseValve(self.systemconfig.daq.ValveSampleDrop)
        self.cardOutput()

    # endregion

    # region 数据
    def obtainResult(self):
        """计算结果，更新到数据库和界面，同时更新界面按钮状态"""
        if self.obtainResults:
            self.vs.calculateResult()
            if self.vs.addAll():
                print("添加数据库成功")
                self.addResult(self.modelResult, self.vs.result)
                self.sigUpdateNewResult.emit(self.vs.result)
            else:
                print("添加数据库失败")
        else:
            self.sigUpdateNewResult.emit(None)

    def dataupdate(self, data):
        if bool(1 - self.enableAcq):
            return
        upcontainer = data[0]
        t = 0.0
        if self.acqCount > 0:
            if upcontainer > self.lastTick:
                t = self.lastTime + (upcontainer - self.lastTick) / float(self.systemconfig.daq.freq)
            else:
                if upcontainer < self.lastTick:
                    t = self.lastTime + (upcontainer + 256 - self.lastTick) / float(self.systemconfig.daq.freq)
        self.lastTime = t
        self.lastTick = upcontainer
        self.vs.addT(t)

        self.acqCount += 1
        v = [0.0 for x in range(0, dataBase.maxChannel)]
        for i in range(dataBase.maxChannel):
            v[i] = data[2][i]
            if t == 0:  # 给各通道均值赋初值
                self.vs.average[i] = v[i]
                # 积分延迟时间，记录均值
            if t < self.config.channels[i].delay:
                if self.config.channels[i].delay > 2:  # 取后2s的均值
                    if t > self.config.channels[i].delay - 2:
                        self.sumDelay[i] += v[i]
                        self.integralDelayCount += 1
                        self.vs.average[i] = self.sumDelay[i] / self.integralDelayCount
                else:
                    self.sumDelay[i] += v[i]
                    self.vs.average[i] = self.sumDelay[i] / self.acqCount
                # 把原始积分延迟曲线记录下来
                self.vs.addValueDelay(i, v[i])
                v[i] = 0
            else:
                v[i] = v[i] - self.vs.average[i]
            if self.analyseStop:
                self.stop[i] = True
            #     记录最大峰值，判断积分结束，加载数据到vs
            if bool(1 - self.stop[i]):
                # 绘制释放曲线
                self.sigCurveLineAddPoint.emit(i, t, v[i])
                self.vs.addValue(i, v[i])
                if v[i] > self.max[i]:
                    self.max[i] = v[i]
                if False and v[i] > self.peakMax:
                    self.stop[i] = True
                    print("超出了最大峰值")
                else:
                    if (t > self.config.channels[i].min and
                            v[i] < self.max[i] * self.config.channels[i].ratio + self.config.channels[
                                i].comporator * 0.001
                            or t > self.config.channels[i].max):
                        self.stop[i] = True

        if False not in self.stop:
            # <editor-fold desc="设置结果通道">
            if self.config.channels[0].enable and self.config.channels[1].enable:
                if self.max[0] > self.config.switch1:  # 高低同时开启，超过切换电压，切换到高通道
                    self.vs.result.Channel = self.vs.result.Channel & 0xFE
                    self.vs.result.Channel = self.vs.result.Channel | 0x02
            elif self.config.channels[0].enable:
                if self.max[0] > self.peakMax:  # 只开启了低通道，超过峰值，复位两个通道，异常！
                    self.vs.result.Channel = self.vs.result.Channel & 0xFC
            # if self.config.channels[2].enable and self.config.channels[3].enable:
            #     if self.max[2] > self.config.switch1:  # 高低同时开启，超过切换电压，切换到高通道
            #         self.vs.result.Channel = self.vs.result.Channel & 0xFB
            #         self.vs.result.Channel = self.vs.result.Channel | 0x08
            # elif self.config.channels[2].enable:
            #     if self.max[2] > self.peakMax:  # 只开启了低通道，超过峰值，复位两个通道，异常！
            #         self.vs.result.Channel = self.vs.result.Channel & 0xF3
            # if self.config.channels[4].enable and self.config.channels[5].enable:
            #     if self.max[4] > self.config.switch1:  # 高低同时开启，超过切换电压，切换到高通道
            #         self.vs.result.Channel = self.vs.result.Channel & 0xEF
            #         self.vs.result.Channel = self.vs.result.Channel | 0x20
            # elif self.config.channels[4].enable:
            #     if self.max[4] > self.peakMax:  # 只开启了低通道，超过峰值，复位两个通道，异常！
            #         self.vs.result.Channel = self.vs.result.Channel & 0xCF
            # </editor-fold>
            for i in range(dataBase.maxChannel):
                self.vs.average[i] = self.vs.average[i] - self.vs.average0[i]
            self.acqEvent.set()  # 将running设置为True，继续线程运行

    # endregion

    # region 采集卡

    def link_card(self):
        self.card.setPortName(self.systemconfig.daq.com)
        self.inverseSignal()
        self.card.link_card()
        if self.card.isLinked:
            self.initCarrierGas()  # TODO 是否考虑换为InitCarrierGas?

    def cardGetDO(self, index):
        """返回指定DO的当前输出值"""
        return self.card.getChannelDO(index)

    def cardGetDI(self, data, index):
        return (data >> index & 0x01) == 0x01

    def cardOpenValve(self, *index):
        for i in index:
            self.card.setChannelDO(i, True)

    def cardCloseValve(self, *index):
        for i in index:
            self.card.setChannelDO(i, False)

    def cardAO(self, index, value, delay=True):
        self.card.writeChannelAO(index, value, delay)

    def cardOutput(self):
        return self.card.outputDO()

    def close_card(self):
        if self.card.isOpen():
            self.analyseStop = True
            self.initPID()
            self.controlTCBridge(False)
            self.controlMFC(False)
            self.card.close()

    def inverseSignal(self):
        """翻转信号"""
        for i in range(dataBase.maxChannel):
            self.card.inverseSignal(self.getADBaseline(i), self.getADBaselineInverse(i))

    # endregion
    def switchNH(self):
        # TODO 定氢模式切换
        pass

    def getresultcolHidden(self):
        return self.systemconfig.resultcolHidden

    def getDAQCom(self):
        return self.systemconfig.daq.com

    def setDAQCom(self, com):
        self.systemconfig.daq.com = com

    def checkLeck(self, type: int):
        if type == 0:  # 整体检漏
            self.cardOpenValve(self.systemconfig.daq.ValveCarrier,
                               self.systemconfig.daq.ValveCompare,
                               self.systemconfig.daq.ValveChamber,
                               )  # 开载气、参比、炉气
            self.cardCloseValve(self.systemconfig.daq.ValveBypass,
                                self.systemconfig.daq.ValvePurge,
                                self.systemconfig.daq.ValveSeal,
                                self.systemconfig.daq.ValveSampleLoad,
                                self.systemconfig.daq.ValveCutoff, )  # 关旁路、冲洗、封顶、加样、排放
            self.cardOutput()
            self.controlMFC(True)
        if type == 1:
            self.cardOpenValve(self.systemconfig.daq.ValveCarrier,
                               )  # 开载气
            self.cardCloseValve(self.systemconfig.daq.ValveBypass,
                                self.systemconfig.daq.ValveCompare,
                                self.systemconfig.daq.ValveChamber,
                                )
            self.cardOutput()
        if type == 2:
            self.cardOpenValve(self.systemconfig.daq.ValveCarrier,
                               self.systemconfig.daq.ValveChamber,
                               )  # 开载气、参比、炉气
            self.cardCloseValve(self.systemconfig.daq.ValveBypass,
                                self.systemconfig.daq.ValveCompare,
                                self.systemconfig.daq.ValvePurge,
                                self.systemconfig.daq.ValveSeal,
                                self.systemconfig.daq.ValveSampleLoad,
                                )  # 关旁路、冲洗、封顶、加样、排放
            self.cardOutput()
            self.controlMFC(False)
        if type == 3:
            pass

    def controlCarrier(self, open: bool):
        if open:
            self.cardOpenValve(self.systemconfig.daq.ValveCarrier)
        else:
            self.cardCloseValve(self.systemconfig.daq.ValveCarrier)
        self.cardOutput()

    def setupRow(self, model, row, r):
        '''
        将DBResult结果更新到model中的指定行
        :param row: 行号
        :param r: 结果
        :return:
        '''
        for j in range(0, dataBase.colNum):
            value = None
            if j == dataBase.dbColId[1]:
                value = r.SampleId.strftime("%Y-%m-%d %H:%M:%S")
            elif j == dataBase.dbColSampleName[1]:
                value = r.SampleName
            elif j == dataBase.dbColSampleNum[1]:
                value = r.SampleNum
            elif j == dataBase.dbColSampleWeight[1]:
                value = r.SampleWeight
            elif j == dataBase.dbColFluxWeight[1]:
                value = r.FluxWeight
            elif j == dataBase.dbColE1[1]:
                value = r.Content01
            elif j == dataBase.dbColE2[1]:
                value = r.Content02
            elif j == dataBase.dbColE3[1]:
                value = r.Content03
            elif j == dataBase.dbColChannel[1]:
                value = ""
                if r.Channel != None:
                    if r.Channel & 0x01 == 0x01:
                        if value != "":
                            value += "-"
                        value += "LO"
                    if r.Channel & 0x02 == 0x02:
                        if value != "":
                            value += "-"
                        value += "HO"
                    if r.Channel & 0x04 == 0x04:
                        if value != "":
                            value += "-"
                        value += "N"
                    if r.Channel & 0x08 == 0x08:
                        if value != "":
                            value += "-"
                        value += "H"
            elif j == dataBase.dbColLE1[1]:
                value = r.Content1
            elif j == dataBase.dbColHE1[1]:
                value = r.Content2
            elif j == dataBase.dbColLE2[1]:
                value = r.Content3
            elif j == dataBase.dbColLE3[1]:
                value = r.Content4
            elif j == dataBase.dbColReLE1[1]:
                value = r.Content10
            elif j == dataBase.dbColReHE1[1]:
                value = r.Content20
            elif j == dataBase.dbColReLE2[1]:
                value = r.Content30
            elif j == dataBase.dbColReLE3[1]:
                value = r.Content40
            elif j == dataBase.dbArea01[1]:
                value = r.Area01
            elif j == dataBase.dbArea02[1]:
                value = r.Area02
            elif j == dataBase.dbArea03[1]:
                value = r.Area03
            elif j == dataBase.dbArea1[1]:
                value = r.Area1
            elif j == dataBase.dbArea2[1]:
                value = r.Area2
            elif j == dataBase.dbArea3[1]:
                value = r.Area3
            elif j == dataBase.dbArea4[1]:
                value = r.Area4
            elif j == dataBase.dbColMethod[1]:
                value = r.MethodName
            elif j == dataBase.dbColUser[1]:
                value = r.UserName
            modelIndex = model.index(row, j)
            model.setData(modelIndex, value, Qt.DisplayRole)
            model.itemFromIndex(modelIndex).setTextAlignment(Qt.AlignHCenter)


onh = ONH()
